package cn.gjxblog.transaction;

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import org.junit.Test;
import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import org.springframework.transaction.interceptor.TransactionProxyFactoryBean;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

/**
 * 声明式事务
 */
public class AnnotationTransactionTests {

    /**
     *  使用TransactionProxyFactoryBean 单独配置每个Bean的代理
     */
    @Test
    public void test1() throws SQLException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(RootConfig.class,TransactionProxyFactoryBeanConfig.class);
        context.refresh();
        UserService userService = (UserService) context.getBean("transactionProxyFactoryBean");
        System.out.println(userService.getClass());
        userService.insertUser();
    }

    /**
     * 采用拦截器+自动代理创建器
     */
    @Test
    public void test2() throws SQLException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(RootConfig.class,TransactionInterceptorConfig.class);
        context.refresh();
        UserService userService = (UserService) context.getBean("userService");
        System.out.println(userService.getClass());
        userService.insertUser();
    }

    /**
     * 采用@Transaction方式
     */
    @Test
    public void test3() throws SQLException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(RootConfig.class,AnnotationTransactionConfig.class);
        context.refresh();

        UserService2 userService = (UserService2) context.getBean("userService");
        System.out.println(userService.getClass());
        userService.insertUser();
    }


    @Configuration
    @EnableTransactionManagement
    static class AnnotationTransactionConfig{
        @Bean
        public UserService2 userService(DataSource dataSource){
            return new UserService2(dataSource);
        }
    }


    @Configuration
    static class TransactionInterceptorConfig{
        @Bean
        public UserService userService(DataSource dataSource){
            return new UserService(dataSource);
        }

        @Bean
        public TransactionInterceptor transactionInterceptor(){
            TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
            // 设置事务管理器
            transactionInterceptor.setTransactionManagerBeanName("transactionManager");
            Properties properties = new Properties();
            properties.setProperty("*","PROPAGATION_REQUIRED");
            // 设置事务属性源
            transactionInterceptor.setTransactionAttributes(properties);
            return transactionInterceptor;
        }

        @Bean
        public BeanNameAutoProxyCreator beanNameAutoProxyCreator(){
            BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
            beanNameAutoProxyCreator.setBeanNames("*Service");
            beanNameAutoProxyCreator.setInterceptorNames("transactionInterceptor");
            return beanNameAutoProxyCreator;
        }
    }

    @Configuration
    static class TransactionProxyFactoryBeanConfig{
        @Bean
        public TransactionProxyFactoryBean transactionProxyFactoryBean(PlatformTransactionManager platformTransactionManager,DataSource dataSource){
            TransactionProxyFactoryBean transactionProxyFactoryBean = new TransactionProxyFactoryBean();
            transactionProxyFactoryBean.setTransactionManager(platformTransactionManager);
            Properties properties = new Properties();
            properties.setProperty("*","PROPAGATION_REQUIRED");
            transactionProxyFactoryBean.setTransactionAttributes(properties);
            transactionProxyFactoryBean.setTarget(new UserService(dataSource));
            return transactionProxyFactoryBean;
        }
    }


    @Configuration
    static class RootConfig{
        @Bean
        public DataSource dataSource(){
            String name ="root";
            String pwd="gjxblog";
            int port = 3306;
            String hostName = "mysql.db.gjxblog.cn";
            DataSource dataSource = new MysqlDataSource();
            ((MysqlDataSource) dataSource).setUser(name);
            ((MysqlDataSource) dataSource).setPassword(pwd);
            ((MysqlDataSource) dataSource).setServerName(hostName);
            ((MysqlDataSource) dataSource).setPort(port);
            ((MysqlDataSource) dataSource).setDatabaseName("test");
            return dataSource;
        }

        @Bean
        public PlatformTransactionManager transactionManager(){
            PlatformTransactionManager transactionManager = new DataSourceTransactionManager(dataSource());
            return transactionManager;
        }

    }


    static class UserService{
        private DataSource dataSource;

        public UserService(DataSource dataSource) {
            this.dataSource = dataSource;
        }

        void insertUser() throws SQLException {
            Statement statement = DataSourceUtils.getConnection(dataSource).createStatement();
            statement.execute("insert into user (`name`,`age`) values ('Mack',22)");
            int i = 10/0;
        }
    }

    static class UserService2{
        private DataSource dataSource;

        public UserService2(DataSource dataSource) {
            this.dataSource = dataSource;
        }

        /**
         * 默认方法修饰符必须为public
         * 原因：
         * @see org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource#computeTransactionAttribute(java.lang.reflect.Method, java.lang.Class)
         */
        @Transactional
        public void insertUser() throws SQLException {
            Statement statement = DataSourceUtils.getConnection(dataSource).createStatement();
            statement.execute("insert into user (`name`,`age`) values ('Mack',22)");
            int i = 10/0;
        }
    }
}
